import { reactive } from 'vue';
import request from '@/api';
import { TablePage, BaseTable, TableRow, ActionType, TableColumn, Dict, DictItem, TableForm } from '@/components/table/index';
import { ElMessage, FormInstance, TableColumnCtx } from 'element-plus';
import { cloneDeep } from 'lodash-es';

/**
 * 表头参数设置
 * */
export interface TableHeaderOptions {
    title?: string;
    buttons?: string[];
}

export interface TableOptions {
    header: TableHeaderOptions;
    form?: TableForm;
    page?: TablePage;
}

// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface TableDataState {}

// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface TableDialogState {}

export class TableState<D extends TableDataState, T extends TableDialogState> {
    // 输出日志
    public log: TableLog = new TableLog('debug');
    // 调用服务API
    public api: TableApi;
    // 表头
    public header: TableHeader = new TableHeader();
    // 表格
    public table: BaseTable = reactive<BaseTable>({
        ref: undefined,
        pk: [],
        data: [],
        loading: false,
        selection: [],
        columns: [],
        emptyText: '',
        pagination: false,
        border: true,
        expandAll: false,
        condition: {},
    });
    // 页面初始化存储数据信息
    public data: D;
    // 页面弹框dialog信息
    public dialog: T;
    // 表格分页
    public page: TablePage = reactive({
        current: 0,
        size: 10,
        total_num: 0,
        pager_count: 5,
        layout: 'sizes, total, prev, pager, next, jumper',
    });
    // 表单状态
    public form: TableForm = reactive({
        ref: undefined,
        labelWidth: 160,
        operate: '',
        items: {},
        submitLoading: false,
        defaultItems: {},
        loading: false,
        param_name: '',
    });
    // 列字典
    public dict: TableColumnDict = new TableColumnDict();
    // 表格动作合辑
    private action: TableAction<D, T> = new TableAction<D, T>();

    /* eslint-disable max-params */
    constructor(
        api: TableApi,
        table: BaseTable,
        data: D,
        dialog: T,
        options: TableOptions = {
            header: {
                title: '标题',
                buttons: [],
            },
            form: {},
            page: {},
        }
    ) {
        this.api = api;
        this.table = Object.assign(this.table, table);
        this.data = Object.assign(reactive<D>(data));
        this.dialog = Object.assign(reactive<T>(dialog));
        options.header.buttons && this.header.setButtons(options.header.buttons);
        options.header.title && this.header.setTitle(options.header.title);
        this.form = Object.assign(this.form, options.form);
    }

    init(fun: (state: TableState<D, T>) => void) {
        fun && fun(this);
    }

    getPK() {
        if (this.table.pk === undefined || this.table.pk.length === 0) {
            return 'id';
        }
        if (this.table.pk.length === 1) {
            return this.table.pk[0];
        }
        return this.table.pk.join('-');
    }

    list() {
        this.table.loading = true;
        this.log.debug('请求列表 list param', this.table.condition);
        this.api
            .post('page', this.table.condition)
            .then((res) => {
                if (res.code === 200) {
                    if (res.data) {
                        this.table.data = res.data.page.rows;
                        this.page.current = res.data.page.current;
                        this.page.size = res.data.page.size;
                        this.page.total_num = res.data.page.total_num;
                    }
                }
                this.table.emptyText = res.data?.msg;
            })
            .catch((error) => {
                this.table.emptyText = `code: ${error.code} msg: ${error.msg}`;
            })
            .finally(() => {
                this.table.loading = false;
            });
    }

    /** 删除 */
    del(rows: TableRow[]) {
        if (this.table.pk === undefined) {
            ElMessage.warning('未设置表格主键');
            return;
        }
        let list: Array<any> = new Array<any>();
        for (let row of rows) {
            let item: any = {};
            for (let pk of this.table.pk) {
                item[pk] = row[pk];
            }
            list.push(item);
        }
        this.api
            .post('delete', { list: list })
            .then((res) => {
                if (res.code === 200) {
                    this.onTableAction('refresh', {});
                }
            })
            .catch((error) => {
                ElMessage.error(`code:${error.code} msg:${error.msg}`);
            });
    }

    /**
     * 双击表格
     * */
    onTableDblclick(row: TableRow, column: TableColumnCtx<TableRow>) {
        console.log('双击', row, column);
    }

    /**
     * 打开表单
     * @param operate 操作:add=添加,edit=编辑
     * @param data 编辑数据
     */
    toggleForm(operate = '', data: any = {}) {
        if (this.form.ref) {
            this.form.ref.resetFields();
        }
        if (operate === 'edit') {
            this.form.items = cloneDeep(data.row);
        } else if (operate === 'add') {
            this.form.items = cloneDeep(this.form.defaultItems);
        }
        this.form.operate = operate;
    }

    /**
     * 提交表单
     * @param formEl 表单组件ref
     */
    onSubmit(formEl: FormInstance | undefined = undefined) {
        // 当前操作的首字母小写
        const operate = this.form.operate!.replace(this.form.operate![0], this.form.operate![0].toLowerCase());
        // 删除为空的属性
        Object.keys(this.form.items!).forEach((item) => {
            if (this.form.items![item] === null) delete this.form.items![item];
        });
        // 表单验证通过后执行的api请求操作
        const submitCallback = () => {
            this.form.submitLoading = true;
            let params: any = {};
            if (this.form.param_name) {
                params[this.form.param_name] = this.form.items;
            }
            this.api
                .post(operate, params)
                .then((res) => {
                    if (res.code === 200) {
                        this.onTableAction('refresh', {});
                        this.toggleForm('');
                    } else {
                        ElMessage.error(`code:${res.code} msg:${res.msg}`);
                    }
                })
                .catch((error) => {
                    ElMessage.error(`code:${error.code} msg:${error.msg}`);
                })
                .finally(() => {
                    this.form.submitLoading = false;
                });
        };

        if (formEl) {
            this.form.ref = formEl;
            formEl.validate().then((valid) => {
                if (valid) {
                    submitCallback();
                }
            });
        } else {
            submitCallback();
        }
    }

    /**
     * 获取表格选择项数组
     */
    getSelection() {
        const rows: TableRow[] = [];
        this.table.selection?.forEach((item) => {
            rows.push(item);
        });
        return rows;
    }

    /**
     * 表格内的事件统一响应
     * @param action 动作: {@link ActionType}
     * @param data 携带数据
     */
    onTableAction(action: string, data: any = {}) {
        this.log.debug('table action', { action, data });
        const fun = this.action.get(action) || this.action.get('default');
        fun!.call(this, data, this);
    }

    loadDict(codes: Array<string>) {
        for (let code of codes) {
            this.dict.load(code);
        }
    }

    /**
     * 表格初始化
     */
    mount() {}

    addAction(type: ActionType, fun: (data: any, state: TableState<D, T>) => void) {
        this.action.add(type, fun);
    }
}

export class TableApi {
    private urls: Map<string, string>;

    constructor(urls: Map<string, string> = new Map<string, string>()) {
        this.urls = urls;
    }

    post(type: string, params: any = {}) {
        let url = this.getUrl(type);
        return request.post(url, params);
    }

    private getUrl(url: string) {
        return this.urls.get(url);
    }
}

export class TableHeader {
    public title = '';
    public buttons: string[] = [];

    setTitle(title: string) {
        this.title = title;
    }

    setButtons(buttons: string[]) {
        this.buttons = buttons;
    }
}

export class TableColumnDict {
    public dictMap: Map<string, Dict> = new Map<string, Dict>();

    load(dictCode: string) {
        request
            .post('/api/dict/queryDict', { dict_code: dictCode })
            .then((res) => {
                if (res.code === 200 && res.data) {
                    let dict: Dict = {
                        dict_code: res.data.dict_code,
                        dict_name: res.data.dict_name,
                        describe: res.data.describe,
                        items: new Array<DictItem>(),
                        map: new Map<string, DictItem>(),
                    };
                    if (res.data.items) {
                        for (let item of res.data.items) {
                            dict.items.push(item);
                            dict.map.set(item.key, item);
                        }
                    }
                    this.dictMap.set(dict.dict_code, dict);
                }
            })
            .catch((error) => {
                console.error(`code: ${error.code},msg: ${error.msg}`);
            });
    }

    getDictItem(dictCode: string): Array<DictItem> {
        if (this.dictMap.has(dictCode)) {
            let dict = this.dictMap.get(dictCode);
            if (dict && dict.items) {
                return dict.items;
            }
        }
        return new Array<DictItem>();
    }

    getDictItemColor(column: TableColumn, row: TableRow, prop: string | undefined) {
        if (prop === undefined) {
            return '#909399';
        }
        if (column.dict_code && this.dictMap.has(column.dict_code)) {
            let dict = this.dictMap.get(column.dict_code);
            if (dict) {
                let key = String(row[prop]);
                if (dict.map) {
                    let dictItem = dict.map.get(key);
                    if (dictItem) {
                        return dictItem.color;
                    }
                }
            }
        }
        return '#909399';
    }

    getDictItemLabel(column: TableColumn, row: TableRow, prop: string | undefined) {
        if (prop === undefined) {
            return '';
        }
        if (column.dict_code && this.dictMap.has(column.dict_code)) {
            let dict = this.dictMap.get(column.dict_code);
            if (dict) {
                let key = String(row[prop]);
                if (dict.map) {
                    let dictItem = dict.map.get(key);
                    if (dictItem) {
                        return dictItem.label;
                    }
                }
            }
        }
        return '';
    }
}

export class TableAction<D extends TableDataState, T extends TableDialogState> {
    private actionFun: Map<ActionType, (data: any, state: TableState<D, T>) => void> = new Map<
        ActionType,
        (data: any, state: TableState<D, T>) => void
    >();

    constructor() {
        this.init();
    }

    add(action: ActionType, fun: (data: any, state: TableState<D, T>) => void) {
        this.actionFun.set(action, fun);
    }

    get(action: ActionType) {
        return this.actionFun.get(action);
    }

    private init() {
        this.add('refresh', (data: any, state: TableState<D, T>) => {
            // 刷新表格在大多数情况下无需置空 data，但任需防范表格列组件的 :key 不会被更新的问题，比如关联表的数据列
            state.table.data = [];
            state.list();
        });
        this.add('add', (data: any, state: TableState<D, T>) => {
            state.toggleForm('add');
        });
        this.add('edit', (data: any, state: TableState<D, T>) => {
            let rows: TableRow[] = state.getSelection();
            if (rows.length === 0) {
                ElMessage.warning('请选择需要编辑记录');
                return;
            }
            if (rows.length > 1) {
                ElMessage.warning('请选择单条编辑记录');
                return;
            }
            state.toggleForm('edit', { row: rows[0] });
        });
        this.add('delete', (data: any, state: TableState<D, T>) => {
            let rows: TableRow[] = state.getSelection();
            if (rows.length === 0) {
                ElMessage.warning('请选择需要删除记录');
                return;
            }
            state.del(rows);
        });
        this.add('field-change', (data: any, state: TableState<D, T>) => {
            if (data.field.render === 'switch') {
                console.log(data);
                if (!data.field || !data.field.prop) return;
                data.row.loading = true;
                let params: any = {};
                data.row[data.field.prop] = data.value;
                if (state.form.param_name) {
                    params[state.form.param_name] = data.row;
                }
                state.api
                    .post('edit', params)
                    .then(() => {
                        ElMessage.success('更新成功');
                    })
                    .catch((error) => {
                        ElMessage.error(`code:${error.code} msg:${error.msg}`);
                    })
                    .finally(() => {
                        data.row.loading = false;
                    });
            }
        });
        this.add('change-show-column', (data: any, state: TableState<D, T>) => {
            let columnKey: any = '';
            let k = 'prop';
            for (const key in state.table.columns) {
                if (state.table.columns[key][k] === data.field) {
                    columnKey = key;
                    return;
                }
            }
            state.table.columns[columnKey].show = data.value;
        });
        this.add('selection-change', (data: any, state: TableState<D, T>) => {
            state.table.selection = data as TableRow[];
        });
        this.add('page-size-change', (data: any, state: TableState<D, T>) => {
            state.table.condition.page_size = data.size;
            state.onTableAction('refresh', { event: 'page-size-change', ...data });
        });
        this.add('current-page-change', (data: any, state: TableState<D, T>) => {
            state.table.condition.page_no = data.page;
            state.onTableAction('refresh', { event: 'current-page-change', ...data });
        });
        this.add('sort-change', (data: any, state: TableState<D, T>) => {
            state.log.warn('sort-change 未实现', data);
        });
        this.add('row-edit', (data: any, state: TableState<D, T>) => {
            state.toggleForm('edit', data);
        });
        this.add('row-delete', (data: any, state: TableState<D, T>) => {
            state.del([data.row]);
        });
        this.add('field-render-change', (data: any, state: TableState<D, T>) => {
            state.log.warn('field-render-change 未实现', data);
        });
        this.add('search', (data: any, state: TableState<D, T>) => {
            state.table.condition.page_no = 1;
            state.table.condition.page_size = state.page.size;
            state.onTableAction('refresh', { event: 'search', ...data });
        });
        this.add('default', (data: any, state: TableState<D, T>) => {
            state.log.warn('No action defined', data);
        });
    }
}

export class TableLog {
    private readonly level: 'debug' | 'warn' | 'error';

    constructor(level: 'debug' | 'warn' | 'error') {
        this.level = level;
    }

    debug(name: string, data: any = {}) {
        if (this.level === 'debug') {
            console.log(name, data);
        }
    }

    warn(name: string, data: any = {}) {
        if (this.level === 'debug' || this.level === 'warn') {
            console.warn(name, data);
        }
    }
}
